//////////////////////////////////////////////////////////////////////
//
//  CryEngine Source code
//	
//	File:Skeleton.cpp
//  Implementation of Skeleton class (Forward Kinematics)
//
//	History:
//	January 12, 2005: Created by Ivo Herzeg <ivo@crytek.de>
//
//////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include <float.h>
#include "CharacterManager.h"
#include "CharacterInstance.h"
#include "FacialAnimation/FacialInstance.h"
#include "LMG.h"

#include "Command_Buffer.h"

#include "ControllerOpt.h"
#include <IJobManager_JobDelegator.h>
#include "PoseModifier/LookAt.h"

// TEMP
std::vector< std::vector<DebugJoint> > g_arrSkeletons;
int32 g_nGlobalAnimID=-1;
int32 g_nAnimID=-1;
uint32 g_AnimationUpdates=0;

DECLARE_JOB("CommandBufferExecute", TCommandBufferExecuteJob, CSkeletonAnimTask::SPU_Execute);

//-----------------------------------------------------------------------------------------------------

struct SLocator
{
	SLocator()
	{
		m_moveSpeed = 0.0f;
		m_turnSpeed = 0.0f;
		m_turnAngle = 0.0f;
		m_slope	 = 0.0f;
		m_translation = Vec3(ZERO);
	}

	~SLocator()	{	}
	f32 m_moveSpeed;
	f32 m_turnSpeed;
	f32 m_turnAngle;
	f32 m_slope;
	Vec3 m_translation;
};

Memory::CPoolFrameLocal* CSkeletonAnimTask::s_pMemoryPool = NULL;
//

bool CSkeletonAnimTask::Initialize()
{
	Memory::CContext& memoryContext = CAnimationContext::Instance().GetMemoryContext();
	s_pMemoryPool = Memory::CPoolFrameLocal::Create(memoryContext, 256000);
	if (!s_pMemoryPool)
		return false;

	return true;
}

//

CSkeletonAnimTask::CSkeletonAnimTask()
{
	m_pSkeletonAnim = NULL;

	m_bProcessed = false;
}

CSkeletonAnimTask::~CSkeletonAnimTask()
{
}

//

void CSkeletonAnimTask::Initialize(CSkeletonAnim& skeletonAnim)
{
	m_pSkeletonAnim = &skeletonAnim;
}

void CSkeletonAnimTask::Begin(const QuatT& locationPhysics, const QuatTS& locationAnimation, bool immediate)
{

	m_locationPhysics = locationPhysics;
	m_locationAnimation = locationAnimation;

	Prepare();
	
	if (Started())
	{
		CryFatalError("Animation Task started while already running!");
		return;
	}

	if (immediate)
	{
		Execute();

		//--- TODO, risk of multiple Synchs from this the immediate mode should be implemented in CAnimationThreadTask instead
		Synchronize();

		return;
	}









		
	TCommandBufferExecuteJob job(cry_rand());
	job.SetClassInstance(this);
	job.SetCacheMode(JobManager::SPUBackend::eCM_8);
	job.RegisterJobState(&m_jobState);
	job.Run();

	CAnimationThreadTask::Begin();
}

void CSkeletonAnimTask::Wait()
{
	DEFINE_PROFILER_FUNCTION();

	if (!Started())
		return;
	
	// wait for task to finish
	gEnv->GetJobManager()->WaitForJob(m_jobState);
	Synchronize();

	CAnimationThreadTask::Wait();
}

void CSkeletonAnimTask::Prepare()
{
	m_bProcessed = false;

	if (!s_pMemoryPool)
		return;

	CSkeletonPose* pSkeletonPose = m_pSkeletonAnim->m_pSkeletonPose;
	if (!pSkeletonPose->PreparePoseDataAndLocatorWriteables(*s_pMemoryPool))
		return;

	m_pSkeletonAnim->PoseModifiersPrepare(m_locationPhysics, m_locationAnimation);
	pSkeletonPose->SynchronizeWithPhysicsPrepare(*s_pMemoryPool, m_locationPhysics, m_locationAnimation);
}

void CSkeletonAnimTask::Synchronize()
{
	CSkeletonPose* pSkeletonPose = m_pSkeletonAnim->m_pSkeletonPose;
	pSkeletonPose->SynchronizePoseDataAndLocatorWriteables();
}

// CAnimationThreadTask

void CSkeletonAnimTask::Execute()
{
	if (!m_pSkeletonAnim->m_pSkeletonPose->m_bFullSkeletonUpdate)
		return; // TODO: Should not even create a task!

	//_controlfp( _EM_INEXACT|_EM_INVALID|_EM_UNDERFLOW|_EM_OVERFLOW,_MCW_EM );
	//_controlfp( _EM_INVALID|_EM_OVERFLOW,_MCW_EM );
	//_controlfp(0, _EM_INVALID|_EM_ZERODIVIDE|_EM_OVERFLOW );
	//_controlfp(0, _EM_INVALID|_EM_ZERODIVIDE);

	CSkeletonPose* pSkeletonPose = m_pSkeletonAnim->m_pSkeletonPose;
	if (!pSkeletonPose->GetPoseDataWriteable())
		return;
	
	PIXBeginNamedEvent(0, "[Anim]Skeleton Task");

	Command::CBuffer commandBuffer;
	m_pSkeletonAnim->Commands_Create(m_locationPhysics, m_locationAnimation, commandBuffer);

	m_pSkeletonAnim->m_IsAnimPlaying = commandBuffer.GetCommandCount() != 0;
	if (m_pSkeletonAnim->m_IsAnimPlaying)
	{
		 	CFacialInstance *pFacialInstance= SPU_MAIN_PTR( (CFacialInstance *)(m_pSkeletonAnim->m_pInstance->CCharInstance::GetFacialInstance()) );
		 	if (pFacialInstance)
		 		pFacialInstance->CFacialInstance::ApplyProceduralFaceBehaviour();
	}
	commandBuffer.Execute();

#ifndef _RELEASE	
	pSkeletonPose->GetPoseData().ValidateRelPose(pSkeletonPose->m_parrModelJoints);
	pSkeletonPose->GetPoseData().ValidateAbsPose(pSkeletonPose->m_parrModelJoints);
#endif

	m_bProcessed = true;

	PIXEndNamedEvent();
}

// SPU

SPU_ENTRY(CommandBufferExecute)
void CSkeletonAnimTask::SPU_Execute(int randomSeed)
{






	CSkeletonAnimTask::Execute();
}

/*
CSkeletonAnim
*/

CSkeletonAnim::CSkeletonAnim()
{
	m_poseModifiers.reserve(16);
	m_poseModifiersPostExecute.reserve(4);
	m_fPostProcessZoomAdjustedDistanceFromCamera = 0.0f;

	m_fDesiredMoveSpeedSmooth=0;
	m_fDesiredMoveSpeedSmoothRate=0;
	m_fDesiredTurnSpeedSmooth=0;
	m_fDesiredTurnSpeedSmoothRate=0;
	m_fDesiredStrafeSmooth=Vec2(0,0);
	m_fDesiredStrafeSmoothRate=Vec2(0,0);
}

CSkeletonAnim::~CSkeletonAnim()
{
	//--- Ensure we clean up & remove all our references
	m_bDidRun = false;
	StopAnimationsAllLayers();
}

//

void CSkeletonAnim::InitSkeletonAnim(CCharInstance* pInstance, CSkeletonPose* pSkeletonPose )
{
	m_pInstance = pInstance;
	m_pSkeletonPose = pSkeletonPose;
	m_CharEditMode = 0;
	m_ShowDebugText = 0;
	m_AnimationDrivenMotion = 0;
	m_MirrorAnimation = 0;

	m_IsAnimPlaying = 0;
	m_ActiveLayer = 0;
	m_bTimeUpdated = false;

	m_desiredLocalLocation.SetIdentity();
	m_desiredArrivalDeltaTime = 0.0f;
	m_desiredTurnSpeedMultiplier = 1.0f;
	m_fDesiredTurnSpeedBlend = 1.0f;
	m_fDesiredTurnSpeedOffset = 0.0f;
	m_fDesiredTurnSpeedScale = 0.0f;
	m_AccVelocity=Vec3(ZERO);

	for (uint32 i = 0; i<eMotionParamID_COUNT; ++i)
	{
		m_desiredMotionParam[i] = 0.0f;
		m_CharEditBlendSpaceOverrideEnabled[i] = false;
		m_CharEditBlendSpaceOverride[i] = 0.0f;
	}

	m_layers[0].m_transitionQueue.SetFirstLayer();


	m_pEventCallback = 0;
	m_pEventCallbackData = 0;

	m_TrackViewExclusive = 0;
	m_bReinitializeAnimGraph = false;
	m_fAllowMultilayerAnim = 1.0f;
	for (int i=0; i<NUM_ANIMATION_USER_DATA_SLOTS; i++)
		m_fUserData[i] = 0.0f;

	m_ActiveLayer = 0;

	m_RelativeMovement.SetIdentity();

	//----------------------------------------------------

	m_bDidRun = false; 
	m_bNeedPostProcess = false; 
	m_pWrongControllerName = NULL; 
	nStopAnimationQueuePos = 0; 
	m_nEventQueuePos = 0;
	m_bSetDefaultPose = 0;

	uint32 numJoints = m_pInstance->m_pModel->m_ModelSkeleton.m_arrModelJoints.size();
	m_arrJointMask.resize(numJoints);
	for (uint32 i=0; i<numJoints; i++)
		m_arrJointMask[i] = 0xFFFF; //activate all bones in all layers

	m_threadTask.Initialize(*this);
}


uint8 CSkeletonAnim::GetJointMask(int32 nJointNo, uint8 layer)
{
	return (m_arrJointMask[nJointNo] >> layer) & 1;
}

void CSkeletonAnim::SetJointMask(int32 nJointNo, uint8 layer, uint8 val)
{
	uint32 mask2 = 1 << layer;
	uint32 mask = (val & 0x1) << layer;
	m_arrJointMask[nJointNo] = (m_arrJointMask[nJointNo] & ~mask2) | mask;
}

//enable & disable animation for one specified bone and layer
bool CSkeletonAnim::SetJointMask(const char* szBoneName, uint32 nLayerNo, uint8 nVal)
{
	if (nLayerNo>=numVIRTUALLAYERS)
		return 0;
	int nBone = m_pSkeletonPose->GetJointIDByName(szBoneName);
	if(nBone == -1)
		return 0;
	SetJointMask(nBone,nLayerNo, nVal); 
	return 1;
}

//enable & disable animation for one full layer
bool CSkeletonAnim::SetLayerMask(uint32 nLayerNo, uint8 nVal)
{
	if (nLayerNo>=numVIRTUALLAYERS)
		return 0;
	uint32 numBones = m_pSkeletonPose->GetJointCount();
	for (uint32 b=0; b<numBones; b++)
		SetJointMask(b,nLayerNo, nVal);
	return 1;
}

void CSkeletonAnim::FinishAnimationComputations()
{
	if (!m_bDidRun)
			return;

		m_threadTask.Wait();
	m_bDidRun = false;

	// TODO: Move this somewhere else
	m_pSkeletonPose->DrawDebugCaps();
	SyncData();
	m_poseModifiers.resize(0);
	m_poseModifiersPostExecute.resize(0);
}

void CSkeletonAnim::SyncData()
{
	DEFINE_PROFILER_FUNCTION();

	// sync other job data
	SyncProcessAnimations();

#ifndef _RELEASE	
		m_pSkeletonPose->GetPoseData().ValidateRelPose(m_pSkeletonPose->m_parrModelJoints);
#endif

	//check if the oldest animation in the queue is still needed
	for(uint32 nVLayerNo=0; nVLayerNo<numVIRTUALLAYERS; nVLayerNo++)
	{
		DynArray<CAnimation>& rAnimations = m_layers[nVLayerNo].m_transitionQueue.m_animations;

		if (rAnimations.size() > 0 && rAnimations[0].m_bRemoveFromQueue)
			RemoveAnimFromFIFO(nVLayerNo, 0, true);
	}

	if (m_bNeedPostProcess & m_threadTask.IsProcessed())
	{	
#ifndef _RELEASE	
		m_pSkeletonPose->GetPoseData().ValidateRelPose(m_pSkeletonPose->m_parrModelJoints);
#endif
		m_pInstance->SkeletonPostProcess(m_PostProcessPhysLocationNext, m_PostProcessAnimLocationNext, m_pPostProcessIAttachment, m_fPostProcessZoomAdjustedDistanceFromCamera, m_nPostProcessOnRender);
		m_bNeedPostProcess = false;
	}
	else
	{
		if (m_pInstance->IsCharacterVisible() || m_pSkeletonPose->m_bFullSkeletonUpdate)
		{
			m_pInstance->BeginSkinningTransformationsComputation();
		}

		// [*DavidR | 24/Jan/2011] Update Fall and Play's standup timer even when the postprocess step is not invoked (usually when it is offscreen), 
		// otherwise it would cause the character's pose to blend during 1 second after it reenters the frustrum even if the standup anim has finished 
		// (see CSkeletonPose::ProcessPhysicsFallAndPlay) 
		// [*DavidR | 24/Jan/2011] ToDo: We may want to update more timers (e.g., lying timer) the same way
		m_pSkeletonPose->m_timeStandingUp += static_cast<float>(__fsel(m_pSkeletonPose->m_timeStandingUp, m_pInstance->m_fOriginalDeltaTime, 0.0f));
	}

	// check if we have deferred queue updates to execute now	
	for( int i = 0 ; i < m_arrDeferredQueueUpdates.size() ; ++i )
	{
		const DeferredQueueUpdate& rQueueUpdate = m_arrDeferredQueueUpdates[i];
		if( rQueueUpdate.m_bRemoveFirstAnim && GetNumAnimsInFIFO(rQueueUpdate.m_nLayer) >= 15)
		{
			RemoveAnimFromFIFO(rQueueUpdate.m_nLayer, 0, true);
		}

		AppendAnimationToQueue( rQueueUpdate.m_nLayer, rQueueUpdate.m_Anim );
	}

	m_arrDeferredQueueUpdates.clear();
}

void CSkeletonAnim::SetPostProcessParameter( const QuatT &rPhysLocationNext, const QuatTS &rAnimLocationNext, IAttachment*	pIAttachment, float fZoomAdjustedDistanceFromCamera, uint32 nOnRender )
{
	m_PostProcessPhysLocationNext = rPhysLocationNext;
	m_PostProcessAnimLocationNext = rAnimLocationNext;
	m_pPostProcessIAttachment = pIAttachment;
	m_fPostProcessZoomAdjustedDistanceFromCamera = fZoomAdjustedDistanceFromCamera;
	m_nPostProcessOnRender = nOnRender;
	m_bNeedPostProcess = true;

}

void CSkeletonAnim::SyncProcessAnimations()
{
	// call defered stopanimation
	for( uint32 i = 0 ; i < numVIRTUALLAYERS ; ++i )
	{
		if( m_arrStopAnimationQueue[i] >= 0 )
		{
			StopAnimationInLayer( m_arrStopAnimationQueue[i], 0.5f );
		}
		else
		{
			break;
		}
	}

	if (m_pEventCallback)
	{
		for( uint32 i=0; i<m_nEventQueuePos; ++i )
		{
			m_LastAnimEvent = spuEventQueue[i];
			(*m_pEventCallback)(m_pInstance,m_pEventCallbackData);
	}
		m_nEventQueuePos = 0;
}

}

//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
void CSkeletonAnim::PoseModifiersPrepare(const QuatT& locationPhysics, const QuatTS& locationAnimation)
{
	Skeleton::CPoseData* pPoseData = m_pSkeletonPose->GetPoseDataWriteable();


	uint32 poseBlenderAimLayer = numVIRTUALLAYERS;
	uint32 poseBlenderLookLayer = numVIRTUALLAYERS;
	if (m_pSkeletonPose->m_PoseBlenderAim.get())
	{
		uint32 old = m_pSkeletonPose->GetPoseBlenderAim().m_blender.m_nDirLayer; //old
		if (old < numVIRTUALLAYERS)
			m_layers[old].m_poseModifier.reset();
		poseBlenderAimLayer = m_pSkeletonPose->GetPoseBlenderAim().m_blender.m_Set_nLayer;
		if (poseBlenderAimLayer < numVIRTUALLAYERS)
			SetPoseModifier(poseBlenderAimLayer, IAnimationPoseModifierPtr());
	}

	if (m_pSkeletonPose->m_PoseBlenderLook.get())
	{
		uint32 old = m_pSkeletonPose->GetPoseBlenderLook().m_blender.m_nDirLayer; //old
		if (old < numVIRTUALLAYERS)
			m_layers[old].m_poseModifier.reset();
		poseBlenderLookLayer = m_pSkeletonPose->GetPoseBlenderLook().m_blender.m_Set_nLayer;
		if (poseBlenderLookLayer < numVIRTUALLAYERS)
			SetPoseModifier(poseBlenderLookLayer, IAnimationPoseModifierPtr());
	}

	if (m_pSkeletonPose->m_bFullSkeletonUpdate && !m_pSkeletonPose->m_bPhysicsRelinquished)
	{
			if (m_pInstance->m_pModel->m_ModelSkeleton.m_useLookIK)
			{
					if (m_pSkeletonPose->m_LookIK().m_UseLookIK || m_pSkeletonPose->m_LookIK().m_LookIKBlend)
							PushLayer(m_pSkeletonPose->m_LookAt);
			}
			
		if (m_pSkeletonPose->m_bGroundAlign && m_pSkeletonPose->m_bGroundAlignment && Console::GetInst().ca_GroundAlignment && m_pSkeletonPose->m_bInstanceVisible)
			PushLayer(cryinterface_cast<IAnimationPoseModifier>(m_pSkeletonPose->m_groundAlignment));

		PushLayer(m_pSkeletonPose->m_limbIk);

		if (m_pSkeletonPose->m_bInstanceVisible && m_pSkeletonPose->m_recoil.get())
			PushLayer(m_pSkeletonPose->m_recoil);

		if (poseBlenderAimLayer < numVIRTUALLAYERS)
		{
			if (m_pSkeletonPose->m_PoseBlenderAim.get())
				SetPoseModifier(poseBlenderAimLayer, cryinterface_cast<IAnimationPoseModifier>(m_pSkeletonPose->m_PoseBlenderAim));
		}
		if (poseBlenderLookLayer < numVIRTUALLAYERS)
		{
			if (m_pSkeletonPose->m_PoseBlenderLook.get())
				SetPoseModifier(poseBlenderLookLayer, cryinterface_cast<IAnimationPoseModifier>(m_pSkeletonPose->m_PoseBlenderLook));
		}
	}

	// TEMP
	//m_pSkeletonPose->PoseModifiersPrepare(locationPhysics, locationAnimation);

	// Proper PoseModifiers initialization. Once everything is refactored away
	// this is the only code that should survive.
	SAnimationPoseModiferParams poseModifierParams;
	poseModifierParams.pCharacterInstance = m_pInstance;
	poseModifierParams.timeDelta = m_pInstance->m_fDeltaTime;
	poseModifierParams.locationNextPhysics = locationPhysics;
	poseModifierParams.locationNextAnimation = locationAnimation;
	poseModifierParams.pPoseRelative = &pPoseData->m_pJointsRelative[0]; // TEMP: Should be NULL.
	poseModifierParams.pPoseAbsolute = &pPoseData->m_pJointsAbsolute[0]; // TEMP: Should be NULL.
	poseModifierParams.jointCount = 0;

	uint32 poseModifierCount = uint32(m_poseModifiers.size());
	for (uint32 i=0; i<poseModifierCount; ++i)
		m_poseModifiers[i]->Prepare(poseModifierParams);

	for (uint32 i=0; i<numVIRTUALLAYERS; ++i)
	{
		if (m_layers[i].m_poseModifier.get())
			m_layers[i].m_poseModifier.get()->Prepare(poseModifierParams);
	}

	poseModifierCount = uint32(m_poseModifiersPostExecute.size());
	for (uint32 i=0; i<poseModifierCount; ++i)
		m_poseModifiersPostExecute[i].get()->Prepare(poseModifierParams);
}






void CSkeletonAnim::PoseModifiersExecutePost(Skeleton::CPoseData& poseData, const QuatT& locationPhysics, const QuatTS& locationAnimation)
{
	SAnimationPoseModiferParams params;
	params.pCharacterInstance = m_pInstance;
	params.timeDelta = m_pInstance->m_fOriginalDeltaTime;
	params.locationNextPhysics = locationPhysics;
	params.locationNextAnimation = locationAnimation;
	params.pPoseRelative = &poseData.m_pJointsRelative[0];
	params.pPoseAbsolute = &poseData.m_pJointsAbsolute[0];
	params.jointCount = poseData.GetJointCount();

	const uint32 poseModifierCount = uint32(m_poseModifiersPostExecute.size());
	for (uint32 i=0; i<poseModifierCount; ++i)
		m_poseModifiersPostExecute[i]->Execute(params);
}


void CSkeletonAnim::PoseModifiersSynchronize()
{
	uint32 poseModifierCount = uint32(m_poseModifiers.size());
	for (uint32 i=0; i<poseModifierCount; ++i)
		m_poseModifiers[i]->Synchronize();

	m_poseModifiers.resize(0);

	for (uint32 i=0; i<numVIRTUALLAYERS; ++i)
	{
		if (m_layers[i].m_poseModifier.get())
			m_layers[i].m_poseModifier.get()->Synchronize();
	}

	poseModifierCount = uint32(m_poseModifiersPostExecute.size());
	for (uint32 i=0; i<poseModifierCount; ++i)
		m_poseModifiersPostExecute[i]->Synchronize();
}


void CSkeletonAnim::ProcessAnimations(const QuatT& rPhysLocationCurr, const QuatTS& rAnimLocationCurr, uint32 OnRender, uint32 UpdateQueue, bool immediate)
{
	if (!Console::GetInst().ca_thread)
		immediate = true;

	if (m_bDidRun)
	{
		CryWarning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, "CSkeletonAnim::ProcessAnimations %s %p process called multiple times a frame", m_pInstance->GetFilePath(), m_pInstance);
		return;
	}

	m_bTimeUpdated = false;

	CCharInstance* pInstance = m_pSkeletonPose->m_pInstance;

	if (pInstance->FacialAnimationEnabled())
	{
		// Only update facial animation if we are close - this will add morph effectors to the list
		// that will last only one frame. Therefore if we don't update them, the morphs will be
		// removed automatically.
		if (m_fPostProcessZoomAdjustedDistanceFromCamera < Console::GetInst().ca_FacialAnimationRadius)
		{
#if USE_FACIAL_ANIMATION_FRAMERATE_LIMITING
			const f32 fraction = (1.0f - m_fPostProcessZoomAdjustedDistanceFromCamera / Console::GetInst().ca_FacialAnimationRadius);
			const uint32 fps = uint32(Console::GetInst().ca_FacialAnimationFramerate * fraction);
#endif

			CFacialInstance* pFaceInst = pInstance->m_pFacialInstance;
			if (pFaceInst)
			{		
#if USE_FACIAL_ANIMATION_FRAMERATE_LIMITING
				pFaceInst->SetTargetFramerate( fps );
#endif
				pFaceInst->Update( m_facialDisplaceInfo, pInstance->m_fDeltaTime, rAnimLocationCurr );
			}

			IAttachmentManager *pAttachMan=pInstance->GetIAttachmentManager();
			int numAttachments=pAttachMan->GetAttachmentCount();
			for (int i=0; i<numAttachments; i++)
			{
				if (IAttachment *pAttach=pAttachMan->GetInterfaceByIndex(i))
				{
					if (IAttachmentObject *pAttachObj=pAttach->GetIAttachmentObject())
					{
						if (ICharacterInstance *pChrInstance=pAttachObj->GetICharacterInstance())
						{
							pFaceInst = (CFacialInstance*)pChrInstance->GetFacialInstance();
							if (pFaceInst)
							{		
#if USE_FACIAL_ANIMATION_FRAMERATE_LIMITING
								pFaceInst->SetTargetFramerate(fps);
#endif
								pFaceInst->Update(m_facialDisplaceInfo, pInstance->m_fDeltaTime, rAnimLocationCurr);
							}
						}
					}
				}
			}

		} // end if (m_fPostProcessZoomAdjustedDistanceFromCamera < Console::GetInst().ca_FacialAnimationRadius)
		else
		{
			m_facialDisplaceInfo.ClearFast();
		}

	} // end if (pInstance->FacialAnimationEnabled())

	if (m_pSkeletonPose->m_bSetDefaultPoseExecute)
	{
		m_pSkeletonPose->SetDefaultPoseExecute(false);
		m_pSkeletonPose->m_bSetDefaultPoseExecute = false;
	}

	Skeleton::CPoseData& poseData = m_pSkeletonPose->GetPoseDataExplicitWriteable();

	if (m_bSetDefaultPose)
	{
		CModelSkeleton* pModelSkeleton =	&m_pInstance->m_pModel->m_ModelSkeleton;
		CModelJoint* parrModelJoints	 =	&pModelSkeleton->m_arrModelJoints[0];
		uint32 numJoints = m_pSkeletonPose->GetJointCount();
		for (uint32 i=0; i<numJoints; i++)
		{
			poseData.m_pJointsRelative[i] = pModelSkeleton->m_poseData.m_pJointsRelative[i];
			poseData.m_pJointsAbsolute[i] = pModelSkeleton->m_poseData.m_pJointsAbsolute[i];
		}
	}
	
		gEnv->pCharacterManager->AddAnimationToSyncQueue( m_pInstance );

	if (GetCharEditMode()==0)
	{
		int nCurrentFrameID = g_pCharacterManager->m_nUpdateCounter; 
		if ( m_pInstance->m_LastUpdateFrameID_Pre == nCurrentFrameID )
		{
			//multiple updates in the same frame can be a problem
			const char* name = m_pInstance->m_pModel->GetModelFilePath();
			g_pISystem->Warning( VALIDATOR_MODULE_ANIMATION,VALIDATOR_WARNING, VALIDATOR_FLAG_FILE,name,	"several pre-updates: FrameID: %x  Old: %x  Now: %x",nCurrentFrameID,m_pInstance->m_LastUpdateFrameID_PreType, OnRender);

			return;
		}
	}

	m_pInstance->m_LastUpdateFrameID_PreType = OnRender;
	if (OnRender!=1)
		m_pInstance->m_LastUpdateFrameID_Pre = g_pCharacterManager->m_nUpdateCounter;

	// clear stopanimation queue
	memset(&m_arrStopAnimationQueue, -1, sizeof(m_arrStopAnimationQueue));
	nStopAnimationQueuePos = 0;
	m_nEventQueuePos = 0;

	m_bDidRun = true;

	//

	ProcessAnimationUpdate(rPhysLocationCurr, rAnimLocationCurr, UpdateQueue);

	m_threadTask.Begin(rPhysLocationCurr, rAnimLocationCurr, immediate);
	if (immediate)
		FinishAnimationComputations();
}


//------------------------------------------------------------------------
//---                        ANIMATION-UPDATE                          ---
//-----    we have to do this even if the character is not visible   -----
//------------------------------------------------------------------------
void CSkeletonAnim::ProcessAnimationUpdate( const QuatT rPhysLocationCurr, const QuatTS rAnimLocationCurr, uint32 UpdateQueue )
{
	DEFINE_PROFILER_FUNCTION();
	
	CSkeletonPose* 	const __restrict pSkeletonPose = m_pSkeletonPose;

	if (pSkeletonPose->m_PoseBlenderAim.get())
	{
		CPoseBlenderAim &rAimIK = pSkeletonPose->GetPoseBlenderAim();

		//rAimIK.m_numActiveDirPoses=0;
		for (uint32 i=0; i<MAX_EXEC_QUEUE*2; i++)
		{
			rAimIK.m_blender.m_DirInfo[i].m_fWeight		= 0.0f;
			rAimIK.m_blender.m_DirInfo[i].m_numDirPoses	= 0;
			rAimIK.m_blender.m_DirInfo[i].m_nGlobalDirID0 = -1;
			rAimIK.m_blender.m_DirInfo[i].m_nGlobalDirID1	= -1;
			rAimIK.m_blender.m_DirInfo[i].m_fAnimTime		= 0.0f;
		}
	}
	
	pSkeletonPose->m_LookIK().m_LookDirection = Vec3(ZERO);
	
	if (Console::GetInst().ca_NoAnim)
		return;	

	//compute number of animation in this layer
	DynArray<CAnimation> &rCurLayer0 = m_layers[0].m_transitionQueue.m_animations;
	uint32 numAnimsInLayer = rCurLayer0.size();
	for (uint32 a=0; a<numAnimsInLayer; a++)
	{
		if (rCurLayer0[a].m_AnimParams.m_nFlags & CA_FORCE_SKELETON_UPDATE)
			UpdateQueue=1; //This means we have to do a full-queue-update
		if (rCurLayer0[a].m_bActivated==0)
			break;
	}

	if (UpdateQueue)
	{
		//float fColor[4] = {1,1,0,1};
		//g_pIRenderer->Draw2dLabel( 1,g_YLine, 2.7f, fColor, false,"QueueUpdate: %s",m_pInstance->GetFilePath());	
		//g_YLine+=26.0f;

		for(uint32 nVLayerNo=0; nVLayerNo<numVIRTUALLAYERS; nVLayerNo++)
		{
			DynArray<CAnimation> &rCurLayer = m_layers[nVLayerNo].m_transitionQueue.m_animations;
			uint32 numAnimsPerLayer = rCurLayer.size();
			if (numAnimsPerLayer)
			{
				BlendManager( m_pInstance->m_fAccDeltaTime*m_layers[nVLayerNo].m_transitionQueue.m_speedMultiplier,  rCurLayer, nVLayerNo );
				if (Console::GetInst().ca_DebugText || m_ShowDebugText)
				{
					BlendManagerDebug(  m_layers[nVLayerNo].m_transitionQueue.m_animations, nVLayerNo );
				}
			}

			LayerBlendManager( m_pInstance->m_fAccDeltaTime, nVLayerNo );
		}

		m_pInstance->m_nForceUpdate =0;

		m_bTimeUpdated = true;

		ParseLocator(numAnimsInLayer);
	}
}


//-----------------------------------------------------------------
//----   Evaluate Locator                                      ----
//-----------------------------------------------------------------
void CSkeletonAnim::ParseLocator(uint32 numAnimsInLayer)
{
	m_RelativeMovement.SetIdentity();
	if (m_AnimationDrivenMotion==0)
		return;

	uint32 acounter=0;
	AnimInfo arrAnimInfo[512];

	DynArray<CAnimation> &rCurLayer = m_layers[0].m_transitionQueue.m_animations;
	uint32 numAnimsPerLayer = rCurLayer.size();
	if (numAnimsPerLayer==0)
		return;

	uint32 numActiveAnims=0;
	for (uint32 a=0; a<numAnimsInLayer; a++)
	{
		if (rCurLayer[a].m_bActivated==0)
			break;
		numActiveAnims++;	
	}
	if (numActiveAnims==0)
		return;

	for (uint32 i=0; i<numActiveAnims; i++)
		ParseLayer0(rCurLayer[i], &arrAnimInfo[0],acounter);

	for (uint32 a=0; a<acounter; a++)
		if (fabsf(arrAnimInfo[a].m_fWeight)<0.01f)	arrAnimInfo[a].m_fWeight=0.0f;
	f32 sum=0.0f;
	for (uint32 a=0; a<acounter; a++)
		sum += arrAnimInfo[a].m_fWeight;
	if (sum==0)
		return;
	for (uint32 a=0; a<acounter; a++)
		arrAnimInfo[a].m_fWeight/=sum;

	SLocator LocatorDst;
	Locator_Evaluation(&arrAnimInfo[0],acounter,&LocatorDst);
	f32 fRelMoveSpeed		= LocatorDst.m_moveSpeed;
	f32 fRelTurnSpeed		= GetRelRotationZ(LocatorDst.m_turnSpeed);
	f32 fRelTravelDir		= LocatorDst.m_turnAngle;
	f32 fRelSlope				= LocatorDst.m_slope;
	Vec3 VecTranslation = LocatorDst.m_translation;
	if (VecTranslation.y<0)
	{
		if (VecTranslation.x<0)
			fRelTravelDir=+gf_PI*0.5f-(fRelTravelDir-gf_PI*0.5f);
		else
			fRelTravelDir=-gf_PI*0.5f-(fRelTravelDir+gf_PI*0.5f);
	}
	Quat q = Quat::CreateRotationZ(fRelTurnSpeed);
	Vec3 t = Quat::CreateRotationZ(fRelTravelDir)*(Quat::CreateRotationX(fRelSlope)*Vec3(0,fRelMoveSpeed,0));
	//Vec3 t = VecTranslation;
	m_RelativeMovement=QuatT(q,t);

	//	float fColor[4] = {0,1,0,1};
	//	g_pIRenderer->Draw2dLabel( 1,g_YLine, 3.3f, fColor, false,"m_RelativeMovement: %f %f %f",m_RelativeMovement.t.x,m_RelativeMovement.t.y,m_RelativeMovement.t.z );
	//	g_YLine+=36.0f;
}

void CSkeletonAnim::ParseLayer0(const CAnimation& rAnim, AnimInfo* pAInfo,uint32& acounter)
{	
	CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;
	if (rAnim.m_fTransitionWeight)
	{
		if (rAnim.m_AnimParams.m_nFlags & CA_FULL_ROOT_PRIORITY)
		{
			for (uint32 a=0; a<acounter; a++)
				pAInfo[a].m_fWeight  = 0;
		}
	}

	if (rAnim.m_Parametric == NULL)
	{

		//regular asset
		const ModelAnimationHeader* pMAG = pAnimationSet->GetModelAnimationHeader(rAnim.m_nAnimID);
		assert(pMAG);
		if (pMAG->m_nAssetType!=CAF_File)
			return;

		int32 nEGlobalID = pMAG->m_nGlobalAnimId;
		GlobalAnimationHeaderCAF& rGlobalAnimHeader = g_AnimationManager.m_arrGlobalCAF[nEGlobalID]; 
		f32 atime	      =	rAnim.m_fAnimTime; 
		int32 segtotal	= rGlobalAnimHeader.m_Segments - 1;
		int32 segcount 	= rAnim.m_nSegmentCounter;
		f32 segdur 			= rGlobalAnimHeader.GetSegmentDuration(segcount);
		f32 totdur 			= rGlobalAnimHeader.m_fTotalDuration ? rGlobalAnimHeader.m_fTotalDuration : 1.0f;
		f32 segbase 		= rGlobalAnimHeader.m_SegmentsTime[segcount];
		f32 percent 		= segdur/totdur;
		f32 time_new		=	atime*percent+segbase;	

		pAInfo[acounter].m_nAnimID			  =	rAnim.m_nAnimID;
		pAInfo[acounter].m_fAnimTime		  =	time_new;												//this is a percentage value between 0-1
		pAInfo[acounter].m_fWeight			  =	rAnim.m_fTransitionWeight;			//this is a percentage value between 0-1
		pAInfo[acounter].m_fPlaybackScale	=	rAnim.m_fCurrentPlaybackSpeed;	//this is a percentage value between 0-1
		acounter++;
	}
	else 
	{
		const SParametric &rCurLocoGroup = *rAnim.m_Parametric;
		f32 fBlendWeights  =	0; 
		for (int32 s=0; s<rCurLocoGroup.m_numAnims; s++)
		{
			f32 fWeight			= rCurLocoGroup.m_fBlendWeight[s]*rAnim.m_fTransitionWeight;
			if (fWeight==0)
				continue;

			int nAnimID		  = rCurLocoGroup.m_nAnimID[s];

		//	float fColor2[4] = {0,1,0,1};
		//	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor2, false,"example %d used weight:%f",nAnimID, fWeight);
		//	g_YLine+=10;

			const ModelAnimationHeader* pMAH = pAnimationSet->GetModelAnimationHeader(nAnimID);
			assert(pMAH);
			int32 nEGlobalID = pMAH->m_nGlobalAnimId;
			assert(pMAH->m_nAssetType==CAF_File);
			GlobalAnimationHeaderCAF& rGlobalAnimHeader = g_AnimationManager.m_arrGlobalCAF[nEGlobalID]; 

			int32 segtotal	= rGlobalAnimHeader.m_Segments - 1;
			int32 segcount 	= rAnim.m_Parametric->m_nSegmentCounter[s];
			f32 segdur 			= rGlobalAnimHeader.GetSegmentDuration(segcount);
			f32 totdur 			= rGlobalAnimHeader.m_fTotalDuration ? rGlobalAnimHeader.m_fTotalDuration : 1.0f;
			f32 segbase 		= rGlobalAnimHeader.m_SegmentsTime[segcount];
			f32 percent 		= segdur/totdur;
			f32 time_new		=	rAnim.m_fAnimTime*percent+segbase;	

			pAInfo[acounter].m_nAnimID				=	nAnimID;
			pAInfo[acounter].m_fAnimTime			=	time_new;		//this is a percentage value between 0-1
			pAInfo[acounter].m_fWeight				= fWeight;
			pAInfo[acounter].m_fPlaybackScale	=	rAnim.m_fCurrentPlaybackSpeed;	//this is a percentage value between 0-1

			acounter++;
			fBlendWeights+=rCurLocoGroup.m_fBlendWeight[s];
		}
		assert( (fBlendWeights==0) || fabsf(fBlendWeights-1.0f)<0.05f );
	}
}


void CSkeletonAnim::Locator_Evaluation(AnimInfo* pAInfo,uint32& acounter2,SLocator* pLocatorDst)
{
	QuatT totalOld(IDENTITY);
	QuatT totalNew(IDENTITY);
	for (uint32 a=0; a<acounter2; a++)	
	{
		if (pAInfo[a].m_fWeight==0.0f)
			continue;
		assert( pAInfo[a].m_nAnimID >=0   );
		CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;
		const ModelAnimationHeader* pMAG = pAnimationSet->GetModelAnimationHeader(pAInfo[a].m_nAnimID);
		assert(pMAG);
		int32 nEGlobalID = pMAG->m_nGlobalAnimId;
		assert(pMAG->m_nAssetType==CAF_File);
		GlobalAnimationHeaderCAF& rCAF = g_AnimationManager.m_arrGlobalCAF[nEGlobalID]; 

		InitSpuControllerLookupTable( rCAF.m_arrControllerLookupVector );

		IController* pRootController = GetRootController(rCAF);

		f32 fDuration		= max(SECONDS_PER_TICK,rCAF.m_fTotalDuration);
		f32 fKeyTime1		= rCAF.NTime2KTime(1);
		f32 fKeyTimeNew = rCAF.NTime2KTime(pAInfo[a].m_fAnimTime);

		//un-timewarp atime and compute proper time of previous frame
		uint32 EOC=0;
		f32 fRealTimeNew = pAInfo[a].m_fAnimTime*fDuration;             
		f32 fRealTimeOld = fRealTimeNew-m_pInstance->m_fDeltaTime*pAInfo[a].m_fPlaybackScale;

		if (fRealTimeOld<0.0f)
		{
			EOC=1;
			fRealTimeOld+=fDuration;
			fRealTimeOld =max(0.0f,fRealTimeOld);
		}
		f32 fKeyTimeOld = rCAF.NTime2KTime(fRealTimeOld/fDuration); 

		if ( pRootController )
		{
			QuatT _new; _new.SetIdentity();
			pRootController->GetOP( fKeyTimeNew, _new.q, _new.t );
			if ((_new.q.v|_new.q.v) < 0.0001f)
				_new.q.SetIdentity();

			QuatT _old; _old.SetIdentity();
			pRootController->GetOP( fKeyTimeOld, _old.q, _old.t );
			if ((_old.q.v|_old.q.v) < 0.0001f)
				_old.q.SetIdentity();

			if (EOC)
			{
				//Special-Case: End of Cycle 
				QuatT EndKey;
				pRootController->GetOP( fKeyTime1, EndKey.q, EndKey.t );
				if ((EndKey.q.v|EndKey.q.v) < 0.0001f)
					EndKey.q.SetIdentity();

				f32 TimeFlow  = m_pInstance->m_fDeltaTime;
				if (TimeFlow<0)
				{
					//time moves backwards
					pRootController->GetOP( fKeyTimeOld, _old.q, _old.t );
					if ((_old.q.v|_old.q.v) < 0.0001f)
						_old.q.SetIdentity();
					_old=EndKey*_old;
				}
				else
				{
					//time moves forward
					pRootController->GetOP( fKeyTimeOld, _old.q, _old.t );
					if ((_old.q.v|_old.q.v) < 0.0001f)
						_old.q.SetIdentity();
					_old=EndKey.GetInverted()*_old;
				}
			}

			const float weight = pAInfo[a].m_fWeight;
			totalOld.q = totalOld.q * _old.q.GetScaled( weight );
			totalOld.t += weight * _old.t;
			totalNew.q = totalNew.q * _new.q.GetScaled( weight );
			totalNew.t += weight * _new.t;
		}
	}

	totalOld.q.NormalizeSafe();
	totalNew.q.NormalizeSafe();
	
	totalOld.q.v.x=0; totalOld.q.v.y=0; totalOld.q.Normalize();
	totalNew.q.v.x=0; totalNew.q.v.y=0; totalNew.q.Normalize();
	
	QuatT totalRel=totalOld.GetInverted()*totalNew;
	
	pLocatorDst->m_moveSpeed   += LocatorHelper::ExtractMoveSpeed(totalRel);
	pLocatorDst->m_turnSpeed   += LocatorHelper::ExtractTurnSpeed(totalRel);
	pLocatorDst->m_turnAngle   += LocatorHelper::ExtractTravelDir(totalRel);
	pLocatorDst->m_slope       += LocatorHelper::ExtractSlope(totalRel);
	pLocatorDst->m_translation += totalRel.t;
}

//-----------------------------------------------------------------------

IController* CSkeletonAnim::GetRootController(GlobalAnimationHeaderCAF& rGAH)
{

	if (rGAH.IsAssetOnDemand())
	{
		assert(rGAH.IsAssetLoaded());
		if (rGAH.IsAssetLoaded()==0)
		{
			return 0;
			//	int nCurrentFrameID = g_pCharacterManager->m_nUpdateCounter; 
			//	CryFatalError("CryAnimation: Asset Not Loaded: %s   nCurrentFrameID: %d  Weight: %f",rCAF.GetFilePath(),nCurrentFrameID,ac.m_fWeight);
		}
	}

	if (rGAH.m_nControllers2)
	{
		if (rGAH.m_nControllers==0)
		{
			uint32 dba_exists=0;
			if (rGAH.m_FilePathDBACRC32)
			{
				size_t numDBA_Files = g_AnimationManager.m_arrGlobalHeaderDBA.size();
				for (uint32 d=0; d<numDBA_Files; d++)
				{
					CGlobalHeaderDBA& pGlobalHeaderDBA = g_AnimationManager.m_arrGlobalHeaderDBA[d];
					if (rGAH.m_FilePathDBACRC32!=pGlobalHeaderDBA.m_FilePathDBACRC32)
						continue;

					dba_exists++;
					break;
				}

			}

#if !defined(__SPU__)
			if (dba_exists)
			{
				if (Console::GetInst().ca_DebugCriticalErrors)
				{
					//this case is virtually impossible, unless something went wrong with a DBA or maybe a CAF in a DBA was compressed to death and all controllers removed
					//	const char* mname = state.m_pInstance->GetFilePath();
					//	f32 fColor[4] = {1,1,0,1};
					//	g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.2f, fColor, false,"model: %s",mname); 
					//	g_YLine+=0x10;
					//	g_pIRenderer->Draw2dLabel( 1,g_YLine, 2.3f, fColor, false,"No Controllers found in Asset: %02x %08x %s",rGAH.m_nControllers2,rGAH.m_FilePathDBACRC32,rGAH.m_FilePath.c_str() );	
					//	g_YLine+=23.0f;
					CryFatalError("CryAnimation: No Controllers found in Asset: %s",rGAH.m_FilePath.c_str());
	}

				g_pISystem->Warning( VALIDATOR_MODULE_ANIMATION,VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, 0 , "No Controllers found in Asset: %s" ,rGAH.m_FilePath.c_str() );
}
#endif

			return 0;  //return and don't play animation, because we don't have any controllers
		}
	}

	const CModelJoint* pModelJoint = &m_pInstance->m_pModel->m_ModelSkeleton.m_arrModelJoints[0];
	return rGAH.GetControllerByJointCRC32( pModelJoint[0].m_nJointCRC32 );
}


//------------------------------------------------------------
//------------------------------------------------------------
//------------------------------------------------------------
void CSkeletonAnim::Serialize(TSerialize ser)
{

	//Disabled. It seems we assign these values to thew wrong instances.
	//This was the reason for Dev_Bug7649. QA confirmed that the game still works without serialization of the queue.
	//I think it is a bad idea to serialize such low-level components. 
	//If it is really necessary to serialize the states, then we should do it at AG-level 

	// Update: Added a cvar to turn it on again and have it tested by QA.
	if (!Console::GetInst().ca_SerializeSkeletonAnim)
		return;



	// make sure no parallel computations are running while serialization
#if !defined(__SPU__)
	FinishAnimationComputations();
#endif

	CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;
	if (ser.GetSerializationTarget() != eST_Network)
	{
		ser.BeginGroup("CSkeletonAnim");
		ser.Value("AnimationDrivenMotion", m_AnimationDrivenMotion );
		ser.Value("ForceSkeletonUpdate", m_pSkeletonPose->m_nForceSkeletonUpdate );

		for (uint32 nLayer=0; nLayer<numVIRTUALLAYERS; nLayer++)
		{
			ser.BeginGroup("FIFO");
			uint32 nAnimsInFIFO = m_layers[nLayer].m_transitionQueue.m_animations.size();  
			ser.Value("nAnimsInFIFO",nAnimsInFIFO);
			if(ser.IsReading())
				m_layers[nLayer].m_transitionQueue.m_animations.resize(nAnimsInFIFO);

			for (uint32 a=0; a<nAnimsInFIFO; a++)
			{
				CAnimation &anim = m_layers[nLayer].m_transitionQueue.m_animations[a];
				anim.Serialize(ser);  

				const ModelAnimationHeader* pAnim = pAnimationSet->GetModelAnimationHeader(anim.m_nAnimID);
				if (ser.IsReading() && (pAnim->m_nAssetType==LMG_File))
				{
					anim.m_Parametric = LMG::AllocateRuntimeParametric();
				}

#ifdef _DEBUG
				assert(pAnim);
				if (anim.m_Parametric != NULL)
				{
					assert(pAnim->m_nAssetType==LMG_File);  //obvious 
				}
#endif

				if (anim.m_Parametric != NULL)
				{
					anim.m_Parametric->Serialize(ser);
				}

				if(ser.IsReading())
				{
					if (anim.m_Parametric == NULL)
					{
						const ModelAnimationHeader& AnimHeader = pAnimationSet->GetModelAnimationHeaderRef(anim.m_nAnimID);
						GlobalAnimationHeaderCAF& rGlobalAnimHeader = g_AnimationManager.m_arrGlobalCAF[AnimHeader.m_nGlobalAnimId];
						if (rGlobalAnimHeader.IsAssetOnDemand())
						{
							uint32 nRootCRC32	= pAnimationSet->m_pModel->m_ModelSkeleton.m_arrModelJoints[0].m_nJointCRC32;
							if (rGlobalAnimHeader.IsAssetLoaded()==0)
								rGlobalAnimHeader.LoadCAF(nRootCRC32);

							rGlobalAnimHeader.m_nRef_at_Runtime++;
						}
					}
					else
					{
						int32 numAnims=anim.m_Parametric->m_numAnims;
						for (int32 i=0; i<numAnims; i++)
						{
							int32 nAnimID   = anim.m_Parametric->m_nAnimID[i];
							if (nAnimID>=0)
							{
								const ModelAnimationHeader& AnimHeader = pAnimationSet->GetModelAnimationHeaderRef(nAnimID);
								GlobalAnimationHeaderCAF& rGlobalAnimHeader = g_AnimationManager.m_arrGlobalCAF[AnimHeader.m_nGlobalAnimId];
								if (rGlobalAnimHeader.IsAssetOnDemand())
								{
									uint32 nRootCRC32	= pAnimationSet->m_pModel->m_ModelSkeleton.m_arrModelJoints[0].m_nJointCRC32;
									if (rGlobalAnimHeader.IsAssetLoaded()==0)
										rGlobalAnimHeader.LoadCAF(nRootCRC32);

									rGlobalAnimHeader.m_nRef_at_Runtime++;
								}
							}
						}
					}
				}
			}
			ser.EndGroup();
		}

		ser.EndGroup();
	}
}
//////////////////////////////////////////////////////////////////////////

void CSkeletonAnim::SetCharEditMode( uint32 m ) 
{	
	m_CharEditMode = m > 0;
	m_pInstance->m_pModel->m_AnimationSet.m_CharEditMode = m;
}; 


//////////////////////////////////////////////////////////////////////////

void CSkeletonAnim::SetDebugging( uint32 debugFlags )
{
	m_ShowDebugText = debugFlags > 0 ;
}


//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
size_t CSkeletonAnim::SizeOfThis()
{
	uint32 TotalSize  = 0;
	TotalSize += sizeof(uint16) * m_arrJointMask.capacity();
	TotalSize += sizeof(DeferredQueueUpdate) * m_arrDeferredQueueUpdates.capacity(); 
	for(uint32 i=0; i<numVIRTUALLAYERS; i++)
		TotalSize += (m_layers[i].m_transitionQueue.m_animations.capacity()*sizeof(CAnimation));
	TotalSize += m_poseModifiers.get_alloc_size();
	TotalSize += m_poseModifiersPostExecute.get_alloc_size();
	return TotalSize;
}

void CSkeletonAnim::GetMemoryUsage(ICrySizer * pSizer) const
{
	pSizer->AddObject( m_arrJointMask );
	pSizer->AddObject( m_arrDeferredQueueUpdates );

	for(uint32 i=0; i<numVIRTUALLAYERS; i++)
		pSizer->AddObject( m_layers[i].m_transitionQueue.m_animations );

	pSizer->AddObject( m_poseModifiers );
	pSizer->AddObject( m_poseModifiersPostExecute );
}

// util function for spus, used to transfer the controller lookuptable to spu local storage
void InitSpuControllerLookupTable( const DynArray<uint32> &lookupTable )
{
























	return;

}

// ugly workaround for a CryCG Bug, sometimes functions marked as SPU_INDIRECT are not mapped,
// one workaround is to move the function into the cpp file, but since ControllerOpt.h doesn't has one,
// i moved it here as a tmp solution till this crycg bug is fixed.
SPU_INDIRECT(CommandBufferExecute(M))
EControllerInfo CControllerOptNonVirtual::GetControllerType()
{
	return eControllerOpt; 
}


CryGUID GUID_AnimationPoseModifier_TransformationPin = CryGUID::Construct(0xcc34ddea972e47daULL, 0x93f9cdcb98c28c8eULL);
void CSkeletonAnim::PushLayer(IAnimationPoseModifierPtr poseModifier)
{
	if (!poseModifier.get())
		return;

	if (poseModifier.get()->GetFactory()->GetClassID() == GUID_AnimationPoseModifier_TransformationPin)
	{
		m_poseModifiersPostExecute.push_back(poseModifier);
	}
	else
	{
		m_poseModifiers.push_back(poseModifier);
	}
}

void CSkeletonAnim::SetTrackViewExclusive( uint32 i )
{
	if (m_TrackViewExclusive && !i)	m_bReinitializeAnimGraph = true;
	m_TrackViewExclusive= i > 0;
}

void CSkeletonAnim::SetTrackViewMixingWeight( uint32 layer, f32 weight )
{
	if (!m_TrackViewExclusive)
	{
		CryLog("Trying to set manual mixing weight without being in TrackViewExclusive mode");
		return;
	}
	m_layers[layer].m_transitionQueue.SetManualMixingWeight(weight);
}

void CTransitionQueue::ApplyManualMixingWeight( uint32 numAnims )
{
	switch (numAnims)
	{
	case 2:
		m_animations[0].m_fTransitionWeight = 1.0f - m_manualMixingWeight;
		m_animations[1].m_fTransitionWeight = m_manualMixingWeight;
		break;
	case 1:
		m_animations[0].m_fTransitionWeight = 1.0f;
		break;
	case 0:
		break;
	default:
		CryLog("Too many ( > 2 ) animations in transition queue in TrackViewExclusive mode");
		}
	}

void CTransitionQueue::SetFirstLayer()
{
	m_additiveWeights = 0.0f;
	m_blending = 1.0f;
	// if this is the first layer, the blending multiplier should not be accessed	
	*(alias_cast<uint32*>(&m_blendingMultiplier)) = F32NAN;	
}

void CTransitionQueue::Reset()
{
	m_additiveWeights = 1.0f;
	m_speedMultiplier = 1.0f;
	m_blending = 0.0f;
	m_blendingTime = 0.0f;
	m_blendingMultiplier = 1.0f;
	m_manualMixingWeight = 0.0f;
}

#include UNIQUE_VIRTUAL_WRAPPER(ISkeletonAnim)
